#ifndef ATL_H
#define ATL_H 1

/*******************************************************************************
 *
 * Voici la librairie des template du projet ADD, ou ADD Template Library
 *
 ******************************************************************************/

#include <iostream>
#include <functional>
#include <vector>
#include <string>

#include <boost/lexical_cast.hpp>

/** @addtogroup Templates
 *  @{
 */

/** Convertit un nombre quelconque en chaîne de caractère.
 * On utilise lexical_cast (librairie boost) pour aller plus vite
 * qu'en utilisant un stringstream.
 *
 * @param[in] type nombre à convertir.
 *
 * @return la chaîne de caractère correspondante.
 */
template <typename T>
std::string ecrireString(const T& type)
{
  return boost::lexical_cast<std::string>(type);
}

/** Permet la lecture de n'importe quel nombre depuis une chaîne de
 * caractère.
 * A nouveau on utilise lexical_cast (boost) pour se simplifier la
 * vie. Ici il faut faire attention au fait que la conversion peut mal
 * se passer, donc on a une structure try - catch.
 *
 *
 * @param[in] str chaîne de caractère à lire.
 * @param[out] type variable dans laquelle le nombre sera stocké.
 *
 * @return \e true si tout s'est bien passé, \e false si une exception
 * est levée.
 */
template <typename T>
bool lireString(const std::string& str, T& type)
{
  try
    {
      type = boost::lexical_cast<T>(str);
    }
  catch(boost::bad_lexical_cast& e)
    {
      std::cerr << e.what() << "\'" << str << "\'" << std::endl;
      return false;
    }
  return true;
}


/**
 * Ce template permet de mettre dans un flot de sortie quelconque
 * (cout, debugstream, fichier) une variable quelconque, pour autant
 * que l'opérateur << soit défini pour ce type.
 *
 * Le template possède un pointeur sur un flot, initialisé lors de la
 * construction, et un méthode operator(), qui permet de remplir le
 * flot et de le retourner.
 *
 * @author Johan "Solo" Luisier
 */
template< typename T, typename typeFlot >
class EcrireElementConteneur : std::unary_function <T,typeFlot&>
{
 public:
  /**
   * Constructeur par défaut, initialise le flot qui sera utilisé.
   *
   * @param[in] os flot de sortie.
   * @param[in] pref préfixe, est ajouté devant chaque valeur, par
   * défaut chaîne vide.
   * @param[in] suf suffixe, est ajouté à la fin de chaque valeur, par
   * défaut on ajout une espace.
   */
 EcrireElementConteneur( typeFlot* os,
			 const std::string& pref = "",
			 const std::string& suf = " " )
   : flot( os ), prefixe( pref ), suffixe( suf )
  {};
  /**
   * Destructeur. On fait un flush, pour être sûr de tout afficher.
   */
  ~EcrireElementConteneur()
    {
      //*flot << std::flush;
    };
  /**
   * Réalise l'affichage (ou le remplissage du fichier) avec la valeur
   * passée en argument.
   *
   * @param[in] type variable qui sera écrite dans le flot.
   *
   * @return le flot de sortie.
   */
  typeFlot& operator()(const T& type) const
    {
      return *flot << prefixe << type << suffixe;
    };
 private:
  /**
   * Pointeur sur le flot utilisé.
   */
  typeFlot* flot;
  /**
   * Est ajouté devant la valeur lors de la sortie sur le flot
   */
  std::string prefixe;
  /**
   * Est ajouté derrière la valeur lors de la sortie sur le flot
   */
  std::string suffixe;
};

#include <functional>

/**
 * Ce template permet d'extraire une clé d'un conteneur du type
 * std::map, de manière générique. Le but est de l'utiliser dans une
 * boucle for_each.
 *
 * @author Johan "Solo" Luisier
 */
template< typename Clef, typename Valeur >
struct  ExtraireClef : std::unary_function < std::pair<Clef, Valeur>, Clef>
{
  /**
   * Renvoie la clé d'un élément d'une std::map.
   *
   * @param[in] paire paire associant un élément de type Clef à un
   * élément de type Valeur. Un tel objet peut être un élément d'un
   * conteneur du type std::map.
   *
   * @return paire.first, qui est du type Clef et est la clé de
   * l'élément du conteneur associatif.
   */
  Clef operator()( const std::pair< Clef, Valeur >& paire )
  {
    return paire . first;
  };
};

#include <functional>

/**
 * Ce template permet d'extraire une valeur d'un conteneur du type
 * std::map, de manière générique. Le but est de l'utiliser dans une
 * boucle for_each.
 *
 * @author Johan "Solo" Luisier
 */
template< typename Clef, typename Valeur >
struct  ExtraireValeur : std::unary_function < std::pair<Clef, Valeur>, Clef>
{
  /**
   * Renvoie la valeur d'un élément d'une std::map.
   *
   * @param[in] paire paire associant un élément de type Clef à un
   * élément de type Valeur. Un tel objet peut être un élément d'un
   * conteneur du type std::map.
   *
   * @return paire.second, qui est du type Valeur et est la valeur de
   * l'élément du conteneur associatif. 
   */
  Valeur operator()( const std::pair< Clef, Valeur >& paire )
  {
    return paire . second;
  };
};

#include <fstream>
#include <functional>

#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/trim.hpp>

#include <boost/assign/list_of.hpp>

#include <boost/foreach.hpp>

/**
 * Ce template permet de lire et appliquer de manière générique les
 * niveaux de sortie d'une classe quelconque.
 *
 * La classe en question doit avoir une méthode static setLevel, qui
 * accepte deux entiers non-signés comme argument, c'est le seul
 * prérequis.
 *
 * La classe est passé comme le premier argument, mais uniquement le
 * type est utilisé.
 *
 * @param[in] noms conteneur des noms des niveaux à utiliser pour
 * configurer la classe passée en premier argument.
 * @param[in] debug permet d'afficher quelques messages de debugging
 * si \e true.
 *
 * @author Johan "Solo" Luisier
 */
template< class T >
void lireNiveaux( T* ptr, const std::vector< std::string >& noms,
		  const bool& debug = false )
{
  std::vector< std::string > test =
    boost::assign::list_of( "ALL" )( "MEM" )("DEBUG" )( "LFUNC" )( "LINFO" )
    ( "FUNC" )( "INFO" )( "BLANK" )( "PROGRESS" )( "RELEASE" )( "WARN" )
    ( "ERR" )( "OFF" );

  std::ifstream entree;
  entree.open( "CONFIG" );

  if ( noms.empty() )
    {
      std::cerr << "La liste des niveaux de sortie est vide pour '"
		<< ptr -> typeObjet() << "'" << std::endl;
      return;
    }

  if ( entree.fail() )
    {
      size_t loc( noms.front().find( "::" ) );
      std::cerr << "Valeur par défaut appliquée pour "
		<< noms.front().substr( 0, loc ) << std::endl;
      return;
    }

  std::string ligne, level;

  std::vector< std::string > mots;
  std::vector< std::string >::const_iterator testIt, testBegin( test.begin() ),
    testEnd( test.end() );

  std::vector< std::string >::const_iterator nomsIt, nomsBegin( noms.begin() ),
    nomsEnd( noms.end() );

  unsigned counter( 0 ), nbrNiveaux( noms.size() );

  if ( debug )
    {
      BOOST_FOREACH( level, noms )
	std::cout << level << ' ';
      std::cout << std::endl;
    }

  while( ! entree.eof() )
    {
      getline( entree, ligne );

      if ( ligne == "" ) continue;
      if ( ligne . at( 0 ) == '#' ) continue;

      boost::algorithm::split( mots, ligne,
			       boost::algorithm::is_any_of( "= " ),
			       boost::algorithm::token_compress_on );

      if ( mots . size() != 2 )
	{
	  std::cerr << "Attention, la ligne est '" << ligne
		    << "', ce qui donne " << mots . size()
		    << " éléments" << std::endl;
	  BOOST_FOREACH( level, mots )
	    std::cerr << "'" << level << "' ";
	  std::cerr << std::endl;
	  continue;
	}

      nomsIt = std::find( nomsBegin, nomsEnd, mots[ 0 ] );

      if ( nomsIt == nomsEnd )
	{
	  if ( debug ) std::cout << "'" << mots[ 0 ]
				 << "' est sauté" << std::endl;
	  continue;
	}

      testIt = std::find( testBegin, testEnd, mots[ 1 ] );

      if ( testIt == testEnd )
	{
	  std::cerr << "Problème le niveau \'" << mots[ 1 ] << "\' est"
		    << " inconnu de lireNiveau" << std::endl;
	  continue;
	}

      if ( debug )
	std::cout << "Le niveau '" << mots[ 0 ] << "' va être changé en '"
		  << mots[ 1 ] << "'" << std::endl;

      T::setLevel( unsigned( testIt - testBegin ),
		   unsigned( nomsIt - nomsBegin ) + 1 );

      if ( ++counter == nbrNiveaux )
	break;
      
      /*
      for ( i = 0; i < nbrNiveaux; i++ )
	{
	  level = "";
	  loc = ligne.find( noms[i], 0 );
	  if ( loc != string::npos )
	    {
	      j = ligne.size() - 1;
	      while( ligne[j] != ' ' )
		{
		  level = ligne[j] + level;
		  j--;
		}
	       for ( j = 0; j < 13; j++ )
		 {
		   if ( test[j] == level )
		     break;
		   
		 }
	       if ( j != 13 )
		 {
		   cerr << noms[i] << " : " << i << endl;
		   T::setLevel( j, i + 1 );
		   //debug() << noms[i] << ' ' << level << endreq;
		   break;
		 }
	    }
	}
      */
    }
}


/** @} */

#endif // ATL_H
